﻿namespace Ngs.Data
{
    #region Using Directives
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using Ngs.Expressions;
    #endregion

    public class ReferenceListMapper<TSource, TDestination, TSubSource, TSubDestination> : AssociationMapper<TSource, TDestination, TSubSource, TSubDestination>, IReferenceListMapper<TSource, TDestination, TSubSource, TSubDestination>
    {

        public ReferenceListMapper()
        {
        }

        #region Properties

        public Func<TDestination, ICollection<TSubDestination>> CollectionFunc { get; set; }
        
        public Func<TSource, IEnumerable<TSubSource>> EnumerableFunc { get; set; }
        
        #endregion

        public Mapper<TSource, TDestination> MapAccessor(Expression<Func<TDestination, ICollection<TSubDestination>>> accessor)
        {
            MemberExpression member = (MemberExpression)accessor.Body;

            // Create replacement parameter
            ParameterExpression parameter = Expression.Parameter(typeof(TSource), accessor.Parameters[0].Name);
            MemberExpression body = Expression.PropertyOrField(parameter, member.Member.Name);
            var lambda = Expression.Lambda<Func<TSource, IEnumerable<TSubSource>>>(body, parameter);

            MapEntityAccessor(lambda);
            MapObjectAccessor(accessor);
            return this;
        }

        public Mapper<TSource, TDestination> MapObjectAccessor(Expression<Func<TDestination, ICollection<TSubDestination>>> accessor)
        {
            ExceptionFactory.ThrowIfArgumentNull(accessor);
            this.CollectionFunc = accessor.Compile();
            return this;
        }

        public Mapper<TSource, TDestination> MapEntityAccessor(Expression<Func<TSource, IEnumerable<TSubSource>>> accessor)
        {
            ExceptionFactory.ThrowIfArgumentNull(accessor);
            this.EnumerableFunc = accessor.Compile();
            return this;
        }

        protected override void ExecuteCore(TSource entity, TDestination obj)
        {
            var entities = EnumerableFunc(entity);
            var objCol = CollectionFunc(obj);

            foreach (var item in entities)
            {
                TSubDestination domainObj = FactoryMethod();
                ReferenceMap(item, domainObj);
                objCol.Add(domainObj);
            }
        }

    }
}
